
/*

Design Pattern Automation Toolkit.
Application to create applications with emphasis on Design patterns.
And support for round trip engineering.
Copyright (C) 2004 Vineeth Neelakant. nvineeth@gmail.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
using System;
using BusinessLayer;
using InfoClasses;
using dpatoolkit.UILayer;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Collections;
using DesignPatternDefinitions;

namespace dpatoolkit.UILayer
{
	/// <summary>
	/// This class represents the graphical class that is displayed
	/// </summary>
	public class UIClass : IUIObject
	{
		/*
		 * The Fileds hash table will be of the form [uml rep string= rectangle]
		 * The methods hashtable will be of [uml rep string=rectangle]
		 */


		#region Fields

		private ClassDiagram parent;
		//cache the previous rectangle and message, optimization!
		private Rectangle cacheInteliRect;
		private string cacheInteliMessage;
		private	Rectangle boundingRect,classRect;
		private	Point dragPoint;
		private bool isAbs;
		private	bool dragging;
		private string className;
		private bool needsComputation = true;
		private Hashtable abstrMethods= new Hashtable(),
			fields=new Hashtable(),
			concreteMethods=new Hashtable();
		/* Remember [relation reference,IsOwner] */
		private 
			ArrayList relations = new ArrayList(),/* relations owned by this uiclass*/
			relations_observers = new ArrayList();/* relations refering this uiclass */
		float maxWidth=0,maxHeight=0;
		int curX,curY;
		Rectangle fieldBoundingRec , methodBoundingRec;
		static Random rand =new Random(0);
		private bool isRemoteObject=false;//class that belongs to another component and has been referenced in the current component.
		private string uiComp; //the ui component to which this uientity belongs.
		private bool drawFields=true, drawMethods=true;
		private System.Windows.Forms.ContextMenu classContextMenu;
		private System.Windows.Forms.MenuItem addDerivedClassMenuItem;
		private System.Windows.Forms.MenuItem menuItem9;
		private System.Windows.Forms.MenuItem fldsMenuItem;
		private System.Windows.Forms.MenuItem mthdsMenuItem;
		private System.Windows.Forms.MenuItem editClsMenuItem;
		private System.Windows.Forms.MenuItem editMthdMenuItem;
		private System.Windows.Forms.MenuItem editFldMenuItem;
		private System.Windows.Forms.MenuItem aggMenuItem;
		private System.Windows.Forms.MenuItem inhMenuItem;
		private System.Windows.Forms.MenuItem refMenuItem;
		private System.Windows.Forms.MenuItem crtMenuItem;
		private System.Windows.Forms.MenuItem delClsMenuItem;

		#endregion

		#region events
		public event UIPositionObserver UIPositionChanged; 

		public void EmitPositionUpdated()
		{
			if( this.UIPositionChanged!=null )
				this.UIPositionChanged(this);
		}
		#endregion

		#region Updatation methods
		public void UpdateAllRelations( IClass cls)
		{
			try
			{
				// a remote object is not supposed to update its
				// relations....only local classes should display 
				// the relations in the current component.
				if(this.isRemoteObject)return;
				this.relations.Clear();
				//this.relations_observers.Clear();
				this.UpdateAggegates(cls);
				this.UpdateCreates(cls);
				this.UpdateInheritance(cls);
				this.UpdateReferences(cls);
			}
			catch(Exception e)
			{
				ErrorMessage.Show(e.ToString());
			}
		}

		public void UpdateFields(IClass cls)
		{
			ArrayList fldList = cls.Fields;
			fields.Clear();
			Rectangle rec=new Rectangle(0,0,1,1);
			if(cls.Fields!=null)
				foreach ( IField fld in fldList )
				{
					//add a rec, which will be update while drawing
					//since recs are value type, no problems :-)
					fields.Add(fld.UMLRepresentation,rec);
				}
			this.needsComputation=true;
            this.ClearIntelliSenseCache();
		}

		public void UpdateMethods(IClass cls)
		{
			ArrayList mthdList = cls.Methods;
			concreteMethods.Clear();
			this.abstrMethods.Clear();
			Rectangle rec= new Rectangle(0,0,1,1);

			if(mthdList!=null)
				foreach ( IMethod mthd in mthdList )
				{

					if( mthd.MethodType == MethodType.Concrete && !this.concreteMethods.Contains(mthd.UMLRepresentation))
						concreteMethods.Add(mthd.UMLRepresentation,rec);
					else if(mthd.MethodType== MethodType.Abstract &&  !this.abstrMethods.Contains(mthd.UMLRepresentation))
						this.abstrMethods.Add(mthd.UMLRepresentation,rec);

				}
			this.needsComputation=true;
            this.ClearIntelliSenseCache();
		}

		public void UpdateInheritance( IClass cls)
		{
			//get all the inheritances and update 
			ArrayList inhlist = cls.InheritedClasses;
			if(inhlist==null || inhlist.Count==0)
				return;
			foreach ( IInheritance inh in inhlist )
			{
				UIInheritance uiinh = new UIInheritance(this,
					this.parent.GetUIObject(inh.BaseClassComponent,inh.BaseClass),
					this.parent.Subject);
			}

		}
		public void UpdateReferences( IClass cls)
		{
			ArrayList reflist = cls.References;
			if(reflist==null || reflist.Count==0)
				return;
			foreach ( IReference rf in reflist)
			{
				IUIRelation refRel;
				if( rf.Order == RelationOrder.Single)
				{
					refRel = new UISingleReference(rf.ReferenceName,
						this,
						this.parent.GetUIObject(
						rf.ReferedComponentName,
						rf.ReferedClassName
						),
						this.parent.Subject);
				}
				else 
					refRel = new UIMultiReference(rf.ReferenceName,
						this,this.parent.GetUIObject(rf.ReferedComponentName,
						rf.ReferedClassName),
						this.parent.Subject);

			}
		}

		public void UpdateAggegates( IClass cls )
		{
			ArrayList agglist = cls.Aggregates;
			if(agglist==null)
				return;
			foreach ( IAggregate agg in agglist)
			{
				IUIRelation aggRel;
				if( agg.Order == RelationOrder.Single)
				{
					aggRel = new UISingleAggregate(agg.AggregateName,this,
						this.parent.GetUIObject(agg.AggregateeClassComponent,
						agg.AggregateeClass),this.parent.Subject);
				}
				else 
					aggRel = new UIMultiAggregate(agg.AggregateName,this,
						this.parent.GetUIObject(agg.AggregateeClassComponent,
						agg.AggregateeClass),this.parent.Subject);

			}
		}

		public void UpdateCreates(IClass cls)
		{
			ArrayList list = cls.Creations;
			if(list==null)
				return;
			foreach ( ICreation cre in list)
			{
				IUIRelation creRel;
				if( cre.Order == RelationOrder.Single)
				{
					creRel = new UISingleCreates(this, this.parent.GetUIObject(cre.CreateeComponentName,
						cre.CreateeClassName),this.parent.Subject);
				}
				else 
					creRel = new UIMultiCreates(this,this.parent.GetUIObject(cre.CreateeComponentName,
						cre.CreateeClassName),this.parent.Subject);

			}
		}

		#endregion

		#region Constructor

		public UIClass(IClass cls,Point pnt,ClassDiagram _p)
		{
			parent = _p;
			className=cls.ClassName;
			isAbs=cls.Abstract;
			dragging = false;
			this.needsComputation=true;

			this.uiComp=cls.ComponentName;
			if(  pnt.X<=0 || pnt.Y<=0 )
			{
				//just create a random point
				//Console.WriteLine("Self correcting.. assigning random locaion to uiclass::"+ cls.ClassName);
				int x=rand.Next()%(this.parent.Width-10);
				int y=rand.Next()%(this.parent.Height-10);
				this.classRect.Location=new Point( x,y);
				//Console.WriteLine("Assigned the points : {0},{1}",x,y);
			}

			else this.classRect.Location= pnt;
			this.UpdateFields(cls);
			this.UpdateMethods(cls);
			this.InitContextMenu();
		}

		private void InitContextMenu()
		{
			this.classContextMenu = new System.Windows.Forms.ContextMenu();
			this.addDerivedClassMenuItem = new MenuItem();
			this.editClsMenuItem = new System.Windows.Forms.MenuItem();
			this.editMthdMenuItem = new System.Windows.Forms.MenuItem();
			this.editFldMenuItem = new System.Windows.Forms.MenuItem();
			this.aggMenuItem = new System.Windows.Forms.MenuItem();
			this.inhMenuItem = new System.Windows.Forms.MenuItem();
			this.refMenuItem = new System.Windows.Forms.MenuItem();
			this.crtMenuItem = new System.Windows.Forms.MenuItem();
			this.delClsMenuItem = new System.Windows.Forms.MenuItem();
			this.menuItem9 = new System.Windows.Forms.MenuItem();
			this.fldsMenuItem = new System.Windows.Forms.MenuItem();
			this.mthdsMenuItem = new System.Windows.Forms.MenuItem();

			// 
			// classContextMenu
			// 
			this.classContextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
													     this.editClsMenuItem,
													     this.editMthdMenuItem,
													     this.editFldMenuItem,
													     this.inhMenuItem,
													     this.aggMenuItem,
													     this.refMenuItem,
													     this.crtMenuItem,
													     this.menuItem9,
													     this.addDerivedClassMenuItem, 
													     this.delClsMenuItem});
			// 
			// editClsMenuItem
			// 
			this.editClsMenuItem.Index = 0;
			this.editClsMenuItem.Text = "Edit Class";
			this.editClsMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// editMthdMenuItem
			// 
			this.editMthdMenuItem.Index = 1;
			this.editMthdMenuItem.Text = "Edit Fields";
			this.editMthdMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// editFldMenuItem
			// 
			this.editFldMenuItem.Index = 2;
			this.editFldMenuItem.Text = "Edit Methods";
			this.editFldMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// aggMenuItem
			// 
			this.aggMenuItem.Index = 4;
			this.aggMenuItem.Text = "Aggregates";
			this.aggMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// inhMenuItem
			// 
			this.inhMenuItem.Index = 3;
			this.inhMenuItem.Text = "Inheritance";
			this.inhMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// refMenuItem
			// 
			this.refMenuItem.Index = 5;
			this.refMenuItem.Text = "References";
			this.refMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// crtMenuItem
			// 
			this.crtMenuItem.Index = 6;
			this.crtMenuItem.Text = "Creates";
			this.crtMenuItem.Click += new System.EventHandler(this.EditMenuItem_Click);
			// 
			// delClsMenuItem
			// 
			this.delClsMenuItem.Index = 8;
			this.delClsMenuItem.Text = "Delete Class";
			this.delClsMenuItem.Click +=new EventHandler(delClsMenuItem_Click);
			// 
			// menuItem9
			// 
			this.menuItem9.Index = 7;
			this.menuItem9.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
												      this.fldsMenuItem,
												      this.mthdsMenuItem});
			this.menuItem9.Text = "Display";
			// 
			// fldsMenuItem
			// 
			this.fldsMenuItem.Index = 0;
			this.fldsMenuItem.RadioCheck = true;
			this.fldsMenuItem.Text = "Fields";
			this.fldsMenuItem.Click += new System.EventHandler(this.fldsMenuItem_Click);
			// 
			// addDerivedClassMenuItem
			// 
			this.addDerivedClassMenuItem.Index = 9;
			this.addDerivedClassMenuItem.Text = "Add a Derived Class of this class";
			this.addDerivedClassMenuItem.Click+=new EventHandler(addDerivedClassMenuItem_Click);
			// 
			// mthdsMenuItem
			// 
			this.mthdsMenuItem.Index = 1;
			this.mthdsMenuItem.RadioCheck = true;
			this.mthdsMenuItem.Text = "Methods";
			this.mthdsMenuItem.Click += new System.EventHandler(this.mthdsMenuItem_Click);

			this.mthdsMenuItem.Checked=this.drawMethods;
			this.fldsMenuItem.Checked=this.drawFields;
		}
		#endregion

		#region UIClass methods and properties


		public bool HideFiledsMethods
		{
			get{return this.drawMethods&&this.drawFields; }
			set
			{
				this.drawMethods=this.drawFields=(bool)value;
				this.needsComputation=true;
			}
		}
		public void SetClassDetails(string cName,bool _abs)
		{
			this.className=cName;
			this.isAbs=_abs;
			this.needsComputation=true;
            this.ClearIntelliSenseCache();
		}
		public bool Abstract
		{
			set
			{ 
				this.isAbs=value;
				this.needsComputation=true;
                this.ClearIntelliSenseCache();
			}
			get
			{
				return this.isAbs;
			}
		}
		public string ClassName
		{
			set
			{
				this.className=value;
				this.needsComputation=true;
                this.ClearIntelliSenseCache();
			}
			get{return this.className;}
		}

		#endregion

		#region IUIObject Members

		public ContextMenu CheckRightClicked(Point p)
		{
			ContextMenu relMenu = new ContextMenu();
			MenuItem lMenuItem = new MenuItem("relation");
			relMenu.MenuItems.Add(lMenuItem);
			/* first check for the relation collision */
			foreach(IUIRelation rel in this.relations)
			{
				if(rel.Contains(p))
				{
					return rel.ContextMenu;
				}
			}
			/* now check for the class rectangle */
			if(this.Contains(p))
				return this.GetContextMenu(p);
			return null;
		}

		public bool ShowMinimal 
		{
			get{return this.drawMethods&&this.drawFields;}
			set
			{
				this.drawMethods=(bool)value;
				this.drawFields=(bool)value;
				this.needsComputation=true;
                this.ClearIntelliSenseCache();
			}
		}
		public ContextMenu GetContextMenu(Point p)
		{
			this.fldsMenuItem.Checked=this.drawFields;
			this.mthdsMenuItem.Checked=this.drawMethods;
			return this.classContextMenu;
		}

		public Point Location
		{
			set{this.classRect.Location=value;}
			get{return this.classRect.Location;}
		}
		public Rectangle BoundingRectangle 
		{
            get
            {
                // the rectangle is inflated by a small amount, so that when relations
                // are drawn, the relation lines end point dont overlap
                // on the class.
                Rectangle incRect = this.boundingRect;
                //incRect.Inflate(1, 1);
                return incRect;
            }
        }

        public bool Contains(Point p)
		{
			return this.boundingRect.Contains(p);
		}

		public bool IsDragging()
		{
			return dragging;
		}

		public void InitDrag(Point p)
		{
			dragPoint.X = p.X - boundingRect.X;
			dragPoint.Y = p.Y - boundingRect.Y;
			dragging = true;
			foreach(IUIRelation relation in this.relations)
				relation.InitDrag();
			foreach(IUIRelation relation in this.relations_observers)
				relation.InitDrag();
		}

		Rectangle pathRec = new Rectangle();

		public void Drag(Point pt, System.Windows.Forms.Control wnd)
		{
			RectangleF recF;
			GraphicsPath path;
			
			foreach(IUIRelation relation in this.relations)
			{
				
//				relation.Invalidate(wnd);
				path = relation.Path;
				recF = path.GetBounds();
				pathRec.Width = (int)recF.Width;
				pathRec.Height = (int) recF.Height;
				pathRec.X = (int)recF.X;
				pathRec.Y = (int)recF.Y;
				pathRec.Inflate(10,10);
				this.parent.Invalidate(pathRec);
			}
			foreach(IUIRelation relation in this.relations_observers)
			{
//				relation.Invalidate(wnd);
				path = relation.Path;
				recF = path.GetBounds();
				pathRec.Width = (int)recF.Width;
				pathRec.Height = (int) recF.Height;
				pathRec.X = (int)recF.X;
				pathRec.Y = (int)recF.Y;
				pathRec.Inflate(10,10);
				this.parent.Invalidate(pathRec);
			}

			pathRec = this.boundingRect;
			pathRec.Inflate(5,5);

			classRect.X = pt.X - dragPoint.X;
			classRect.Y = pt.Y - dragPoint.Y;

			this.parent.Invalidate(pathRec);
		}

		public void EndDrag()
		{
			dragging = false;
			this.needsComputation=true;
			int offset=10;						
			//if the user has dragged the class beyond the class area,bring it into view
            if (this.boundingRect.X < parent.ClientRectangle.X)
			{
				this.classRect.X=offset;
			}
            if (this.boundingRect.X + this.boundingRect.Width > parent.ClientRectangle.X + parent.ClientRectangle.Width)
			{
				this.classRect.X=0+ parent.Size.Width-this.classRect.Width-offset;
			}
            if (this.boundingRect.Y < parent.ClientRectangle.Y)
			{
				this.classRect.Y=offset;
			}
            if (this.boundingRect.Y + this.boundingRect.Height > parent.ClientRectangle.Y + parent.Size.Height)
			{
				this.classRect.Y=0+parent.Size.Height-this.boundingRect.Height-offset;
			}
			foreach(IUIRelation relation in this.relations)
				relation.EndDrag();
			foreach(IUIRelation relation in this.relations_observers)
				relation.EndDrag();
		}

		public bool CanDrag()
		{
			return false;
		}

        public ArrayList Relations
        {
            get { return this.relations; }
        }
		public void Draw(Graphics g)
		{
			//compute the UI position only when it is dragged :) optimization
			if (this.dragging || this.needsComputation)
				this.ComputeUIRecs(g);

			/* If the class is not being dragged,
			 * draw the complete contents, this is done in order
			 * to avoid lot of overhead during drawing, bcos drawing
			 * all these takes lot of rescources */
			if(!this.dragging)
			{
                g.FillRectangle(UIRenderer.ClassBrush(this.boundingRect), this.boundingRect);
				this.DrawClassRectangle(g);
                this.DrawMethods(g, this.concreteMethods,
                    UIRenderer.ConcreteMethodFont, UIRenderer.UIStringFormat);
                this.DrawMethods(g, this.abstrMethods,
                    UIRenderer.AbstractMethodFont, UIRenderer.UIStringFormat);
				this.DrawFields(g);
				if( !this.drawMethods || !this.drawFields)
					g.DrawRectangle(Pens.Brown,this.boundingRect);
				else
                    g.DrawRectangle(UIRenderer.ActiveClassBoundaryPen, this.boundingRect);
			}	
			else
			{
				g.DrawRectangle(Pens.SlateGray, this.boundingRect);
				g.DrawString(className, 
					UIRenderer.ConcreteClassFont,
					Brushes.SlateGray, 
					this.boundingRect, 
					UIRenderer.ClsUIStringFormat
					);
			}
		}
		public void DrawRelations (Graphics g)
		{
			if(this.UIPositionChanged!=null)
			{
				this.UIPositionChanged(this);
			}
			this._DrawRelations(g);
		}

        public void DrawShadow(Graphics g)
        {
            if (!this.dragging)
            {
                Rectangle shadowRect = this.boundingRect;
                shadowRect.X += 3;
                shadowRect.Y += 3;
                g.FillRectangle(Brushes.LightGray, shadowRect);
            }
        }

		protected int _GetUIOffset(int interval,IUIRelation relation)
		{
			int index;
			/* first find in owned relations */
			index = this.relations.IndexOf(relation);

			if(index == -1)
			{
				/* find in non owned relations */
				index = this.relations_observers.IndexOf(relation);
				if(index ==-1)
				{
					Console.WriteLine("Relation not found, UI Error for class" + this.className);
					return 0;
				}
				return interval * ( (index+1) +this.relations.Count );

			}
			return interval * (index+1);
		}
		public int GetUIXoffset(IUIRelation relation)
		{
			if(this.TotalRelations == 0)
				return 0;
			int xinterval = this.boundingRect.Width/(this.TotalRelations+1);
			return this._GetUIOffset(xinterval,relation);
			
		}
		public int GetUIYoffset(IUIRelation relation)
		{
			if(this.TotalRelations == 0)
				return 0;
			int yinterval = this.boundingRect.Height/(this.TotalRelations+1);
			return this._GetUIOffset(yinterval,relation);
		}

		public string GetIntellisenseInfo(Point p)
		{
			if( this.cacheInteliRect.Contains(p) )
				return this.cacheInteliMessage;

			this.cacheInteliMessage=string.Empty;

			IDesignPatternSubject subj=this.parent.Subject;
			IProject proj = subj.GetProject();
			IClass cls = proj.GetClass(this.UIComponent,this.UIName);
			Hashtable metainfo;
			IDictionaryEnumerator it1,it2;
			if(classRect.Contains (p))
			{					
				if(this.isAbs)
					this.cacheInteliMessage+="[Abstract Class]\n";
				if(this.isRemoteObject)
				{
					this.cacheInteliMessage+="Remote Class, imported from component:"+this.uiComp;
					this.cacheInteliMessage+="\n";
				}
				else 
				{
					this.cacheInteliMessage+="Component:"+this.uiComp;
					this.cacheInteliMessage+="\n";
				}
				if(!this.drawMethods)
					this.cacheInteliMessage+="[methods hidden. middle click to view]" + "\n";
				if(!this.drawFields)
					this.cacheInteliMessage+="[fields hidden. middle click to view]" + "\n";

                if (cls.ClassSummary.Length > 0)
                {
                    this.cacheInteliMessage += cls.ClassSummary;
                }

				metainfo = cls.GetAllMetaInfo();
                if (metainfo.Count > 0)
                {
                    this.cacheInteliMessage += "\n[MetaInfo]\n";
                    this.cacheInteliMessage += GetMetaInfoStr(metainfo);

                }

				this.cacheInteliRect=this.classRect;
				return this.cacheInteliMessage;
			}

			//now check for methods.
			//concrete methods
			it1 = this.concreteMethods.GetEnumerator();
			while ( it1.MoveNext() )
			{
				Rectangle cmrect = (Rectangle)it1.Value ;//as Rectangle;
				if (cmrect.Contains (p) ) 
				{
					foreach(IMethod mthd in cls.Methods)
					{
						if(mthd.UMLRepresentation==(string)it1.Key)
						{
                            if (mthd.MethodSummary.Length != 0)
                            {
                                this.cacheInteliMessage += mthd.MethodSummary;
                            }
							metainfo = mthd.GetAllMetaInfo();
                            if (metainfo.Count > 0)
                            {
                                this.cacheInteliMessage += "\n[MetaInfo]\n";
                                this.cacheInteliMessage += GetMetaInfoStr(metainfo);
                            }
							this.cacheInteliRect=cmrect;
							return this.cacheInteliMessage;
						}

					}
				}
			}
			//abstract methods
			it1 = this.abstrMethods.GetEnumerator();
			while ( it1.MoveNext() )
			{
				Rectangle amrect = (Rectangle)it1.Value ;//as Rectangle;
				if (amrect.Contains (p) ) 
				{					
					foreach(IMethod mthd in cls.Methods)
					{
						if(mthd.UMLRepresentation==(string)it1.Key)
						{
                            this.cacheInteliMessage += "[Abstract Method]\n";
                            if (mthd.MethodSummary.Length != 0)
                            {
                                this.cacheInteliMessage += mthd.MethodSummary;
                            }
							metainfo = mthd.GetAllMetaInfo();
                            if (metainfo.Count > 0)
                            {
                                this.cacheInteliMessage += "\n[MetaInfo]\n";
                                this.cacheInteliMessage += GetMetaInfoStr(metainfo);
                            }

							this.cacheInteliRect=amrect;
							return this.cacheInteliMessage;
						}

					}

				}
			}
			//check for fields
			it1 = this.fields.GetEnumerator();
			while ( it1.MoveNext() )
			{
				Rectangle frect = (Rectangle)it1.Value ;
				if (frect.Contains (p) ) 
				{

					foreach(IField fld in cls.Fields)
					{
						if(fld.UMLRepresentation==(string)it1.Key)
						{
                            if (fld.FieldSummary.Length != 0)
                            {
                                this.cacheInteliMessage += fld.FieldSummary;
                            }
                            metainfo = fld.GetAllMetaInfo();
                            if (metainfo.Count > 0)
                            {
                                this.cacheInteliMessage += "\n[MetaInfo]\n";
                                this.cacheInteliMessage += GetMetaInfoStr(metainfo);
                            }
							this.cacheInteliRect=frect;
							return this.cacheInteliMessage;
						}
					}
				}
			}
            this.ClearIntelliSenseCache();
            return this.cacheInteliMessage;
		}

		public int AddUIPositionObserver(IUIRelation rel, UIPositionObserver posDelegate,bool isOwner)
		{
			this.UIPositionChanged+=posDelegate;
			this.EmitPositionUpdated();
			if(isOwner)
			{
				this.relations.Add(rel);
				return this.relations.IndexOf(rel);
			}
			else
			{
				this.relations_observers.Add(rel);
				return this.relations.Count +
					this.relations_observers.IndexOf(rel);
			}
		}
		public void RemoveUIPositionObserver(IUIRelation rel, UIPositionObserver posDelegate)
		{
			this.UIPositionChanged -= posDelegate;
			if(this.relations.Contains(rel))
				this.relations.Remove(rel);
			else
				this.relations_observers.Remove(rel);
		}
		public bool RemoteObject
		{
			get{return this.isRemoteObject;}
			set{this.isRemoteObject=value;}
		}
		public string UIComponent
		{
			get{return this.uiComp;}
			set{this.uiComp=(string)value;}
		}

		public string UIName
		{
			get{return this.ClassName;}
			set{this.ClassName=(string)value;}
		}

		public int TotalRelations
		{
			get{return this.relations.Count + this.relations_observers.Count;}
		}
		public int GetIndex(IUIRelation relation)
		{
			if(this.relations.Contains(relation))
				return this.relations.IndexOf(relation);
			else if(this.relations_observers.Contains(relation))
				return this.relations_observers.IndexOf(relation);
			else
				Console.WriteLine("GetIndex : relation not found");
			return 0;

		}
		#endregion
        
        protected void ClearIntelliSenseCache()
        {
            this.cacheInteliRect.X = this.cacheInteliRect.Y = -100; //clear the cache rect;
            this.cacheInteliMessage = String.Empty;
        }

		#region Drawing Logic
		private void DrawClassRectangle(Graphics g)
		{
			Font fnt;
			if(this.isAbs)  
				fnt= UIRenderer.AbstractClassFont;
			else
				fnt = UIRenderer.ConcreteClassFont;

            //if (!this.isRemoteObject)
            //    g.FillRectangle(UIRenderer.ClassBrush(this.classRect), classRect);
            //else
            //    g.FillRectangle(UIRenderer.RemoteClassBrush(this.classRect), classRect);

			g.DrawString(className, 
				fnt, 
				Brushes.Black, 
				classRect, 
				UIRenderer.ClsUIStringFormat
				);
			//g.DrawRectangle(SystemPens.Highlight, this.classRect);
		}

        private void DrawMethods(Graphics g, Hashtable methods, Font f, StringFormat sf)
        {
            if (!this.drawMethods) return;
            if (methods.Count == 0) return;
            
            IDictionaryEnumerator enumerator = methods.GetEnumerator();
            bool firstRectangle = true;
            while (enumerator.MoveNext())
            {
                //we will use IMethod interface here for:
                //Finding the type of method...and selecting the brush and font.
                //Get UML Representation.
                Rectangle rec = (Rectangle)enumerator.Value;
                if (firstRectangle)
                {
                    firstRectangle = false;
                    g.DrawLine(Pens.Black, rec.Left, rec.Top, rec.Right, rec.Top);
                }
                //g.FillRectangle(UIRenderer.ConcreteMethodBrush(rec), rec);

                g.DrawString(enumerator.Key as string,
                    f,
                    Brushes.Black,
                    rec,
                    UIRenderer.UIStringFormat
                    );

                //g.DrawRectangle(SystemPens.Highlight, rec);
            }

        }
		
		private void DrawFields(Graphics g)
		{
			if( !this.drawFields) return;
			if(this.fields.Count<1) return;

			Font f = UIRenderer.FieldFont;
			StringFormat sf= UIRenderer.UIStringFormat;
			IDictionaryEnumerator enumerator = 
				this.fields.GetEnumerator();
            bool firstRectangle = true;
			while ( enumerator.MoveNext() )
			{
				Rectangle rec = (Rectangle) enumerator.Value;
                if (firstRectangle)
                {
                    firstRectangle = false;
                    g.DrawLine(Pens.Black, rec.Left, rec.Top, rec.Right,rec.Top);
                }
				//g.FillRectangle(UIRenderer.FieldBrush(rec), rec);

				g.DrawString(enumerator.Key as string, 
					f, 
					Brushes.Black, 
					rec, 
					UIRenderer.UIStringFormat
					);

				//g.DrawRectangle(SystemPens.Highlight, rec);
			}
		}

		private void _DrawRelations( Graphics g)
		{
			if(this.relations.Count == 0)
				return;
			/* draw only the relations owned by this uiclass */
			foreach ( IUIRelation rel in this.relations )
				rel.Draw(g);
		}

		public void ComputeUIRecs( Graphics g )
		{
			//first compute the Class Rectangle
			this.maxWidth=-1;
			this.maxHeight=-1;
			Font fnt;
			String longestStr=string.Empty;
			if(this.isAbs)  
				fnt= UIRenderer.AbstractClassFont;
			else
				fnt = UIRenderer.ConcreteClassFont;

			SizeF size=UIRenderer.MeasureString(className,g,fnt);
			if (size.Width > this.maxWidth)
				this.maxWidth=size.Width;
			if (size.Height > this.maxHeight)
				this.maxHeight=size.Height;
			IDictionaryEnumerator enumerator;	
			if (this.drawMethods)
			{
				//computations for the concrete Method Rectangles
				fnt = UIRenderer.ConcreteMethodFont;
				enumerator = this.concreteMethods.GetEnumerator();

				while ( enumerator.MoveNext() )
				{
					//Find the the longest string and compute the corressponding rectangle
					string str = enumerator.Key as string;
					if (str.Length > longestStr.Length) longestStr=str;
				}

				size = UIRenderer.MeasureString(longestStr,g,fnt);					
				if (size.Width > this.maxWidth) 
					this.maxWidth=size.Width;
				if(size.Height >this.maxHeight)
					this.maxHeight=size.Height;

				//computations for the Abstract Method Rectangles
				longestStr=String.Empty;
				fnt = UIRenderer.AbstractMethodFont;
				enumerator = this.abstrMethods.GetEnumerator();
				while ( enumerator.MoveNext() )
				{
					//Find the the longest string and compute the corressponding rectangle
					string str = enumerator.Key as string;
					if (str.Length > longestStr.Length) longestStr=str;
				}

				size = UIRenderer.MeasureString(longestStr,g,fnt);					
				if (size.Width > this.maxWidth) 
					this.maxWidth=size.Width;
				if(size.Height >this.maxHeight)
					this.maxHeight=size.Height;
			}	
			if( this.drawFields)
			{
				//computations for the Field Rectangles
                longestStr = String.Empty;
				fnt = UIRenderer.FieldFont;
				enumerator = this.fields.GetEnumerator();
				while ( enumerator.MoveNext() )
				{
					string str = enumerator.Key as string;
					if (str.Length > longestStr.Length) longestStr=str;
				}

				size = UIRenderer.MeasureString(longestStr,g,fnt);					
				if (size.Width > this.maxWidth) 
					this.maxWidth=size.Width;
				if(size.Height >this.maxHeight)
					this.maxHeight=size.Height;
			}
			//Now update the Rectangle values.

			//For the class Rectangle.
			this.classRect.Width=(int)this.maxWidth;
			this.classRect.Height=(int)this.maxHeight;
			this.curX=this.classRect.X;
			this.curY=this.classRect.Y+(int)this.maxHeight;
			Hashtable temp = new Hashtable();//since the collection cant be modified, use this!
			Rectangle rec = new Rectangle(0,0,1,1);
			if(this.drawMethods)
			{
				//For the concrete Method Rectangles.
				foreach( string key in this.concreteMethods.Keys)
				{
					temp.Add(key,null);
				}

				foreach (string key in temp.Keys)
				{
					rec.X=this.curX;
					rec.Y=this.curY;
					rec.Width=(int)this.maxWidth;
					rec.Height=(int)this.maxHeight;
					this.concreteMethods[key]=rec;
					this.curY+=(int)this.maxHeight;
				}

				//For the abstract Method Rectangles.
				temp.Clear();
				foreach( string key in this.abstrMethods.Keys)
				{
					temp.Add(key,null);
				}

				foreach (string key in temp.Keys)
				{

					rec.X=this.curX;
					rec.Y=this.curY;
					rec.Width=(int)this.maxWidth;
					rec.Height=(int)this.maxHeight;
					this.abstrMethods[key]=rec;
					this.curY+=(int)this.maxHeight;
				}
			}
			if(this.drawFields)
			{
				temp.Clear();
				//For the Field rectangles   
				foreach( string key in this.fields.Keys)
				{
					temp.Add(key,null);
				}

				foreach (string key in temp.Keys)
				{
					rec.X=this.curX;
					rec.Y=this.curY;
					rec.Width=(int)this.maxWidth;
					rec.Height=(int)this.maxHeight;
					this.fields[key]=rec;
					this.curY+=(int)this.maxHeight;
				}
			}

			if(this.drawMethods)
			{
				this.methodBoundingRec.X=this.classRect.X;
				this.methodBoundingRec.Y=this.classRect.Y+this.classRect.Height;
				this.methodBoundingRec.Width = (int)this.maxWidth ;
				this.methodBoundingRec.Height = (this.concreteMethods.Count+ this.abstrMethods.Count) * (int)this.maxHeight;
			}
			if(this.drawFields)
			{
				this.fieldBoundingRec.X=this.methodBoundingRec.X;
				this.fieldBoundingRec.Y=this.methodBoundingRec.Y+this.methodBoundingRec.Height;
				this.fieldBoundingRec.Width=(int)this.maxWidth;
				this.fieldBoundingRec.Height=this.fields.Count * (int)this.maxHeight;
			}

			this.boundingRect.X=this.classRect.X;
			this.boundingRect.Y=this.classRect.Y;
			this.boundingRect.Width=(int)this.maxWidth;
			this.boundingRect.Height=(int)this.maxHeight * ( 1/*for the class rectangle*/);
			if(this.drawMethods)
				this.boundingRect.Height+=(int)this.maxHeight * (concreteMethods.Count+ this.abstrMethods.Count);
			if(this.drawFields)
				this.boundingRect.Height+=(int)this.maxHeight* (fields.Count);

			//clear the computation request
			this.needsComputation=false;
		}

		#endregion

		#region Menu Event Handlers
		private void EditMenuItem_Click(object sender, System.EventArgs e)
		{
			this.parent.Subject.ShowClassEditor(this.UIComponent,this.UIName,(sender as MenuItem).Index+1);
		}

		private void fldsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.drawFields=!this.drawFields;
			this.needsComputation=true;
			this.parent.Refresh();
		}

		private void mthdsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.drawMethods=!this.drawMethods;
			this.needsComputation=true;
			this.parent.Refresh();
		}

		private void delClsMenuItem_Click(object sender, EventArgs e)
		{
			this.parent.Subject.DeleteClass(this.UIComponent,this.UIName);
		}

		private void addDerivedClassMenuItem_Click(object sender, EventArgs e)
		{
			//the location is chosen such that the new derived class comes directly below the base class. 
			UIClass derClass = this.parent.CreateNewClass(new Point(this.Location.X , this.Location.Y+this.boundingRect.Height + 50));
			if(derClass==null)
				return;
			//now add the inheritance relation.
			IInheritance inh = new InfoClasses.InheritanceInfo();
			inh.BaseClass=this.UIName;
			inh.DerivedClass= derClass.UIName;
			inh.BaseClassComponent = this.UIComponent;
			inh.DerivedClassComponent = derClass.UIComponent;
			this.parent.Subject.AddInheritance(inh,true);
		}
		#endregion

        private static string GetMetaInfoStr(Hashtable metainfo)
        {
            IDictionaryEnumerator it;
            string metaInfoStr = string.Empty;
            it = metainfo.GetEnumerator();
            while (it.MoveNext())
            {
                metaInfoStr += "[" + (string)it.Key + "] ";
                metaInfoStr += (string)it.Value + "\n";
            }
            return metaInfoStr;
        }

	}
}


